
#include "Proc.h"
#include <cassert>
#include "myLog.h"
#include <errno.h>
#include "Misc.h"
#include <cstring>

bool setThreadName(const std::string &name, const pthread_t tid) 
{
    // limits is 16bytes include '\0'
    const size_t nameSizeLimit = 15;
    if (name.size() > nameSizeLimit || 0 == name.size()) 
    {
        qlog_e("invalid thread name, ensure the content in the range [1, %d].", nameSizeLimit);
        return false;
    }
    char realName[nameSizeLimit + 1] = { 0 };
    strcpy(realName, name.c_str());
    auto ret = pthread_setname_np(tid, realName);
    if (0 != ret) 
    {
        qlog_e("set thread name failed.");

        return false;
    }

    return true;
}

bool setProcPriority(const int prior,const enum __priority_which type)
{
    // the range is -20 to 19
    if(prior < PRIO_MIN || prior >= PRIO_MAX)
    {
        qlog_e("invalid priority value: %d, valid range is [%d,%d]\n",PRIO_MIN,PRIO_MAX);
        return false;
    }

    auto ret = setpriority(static_cast<int>(type),0,prior);
    if(ret < 0)
    {
        int err = errno;
        qlog_e("set priority(%s) failed, %s\n",type == __priority_which::PRIO_PROCESS ? "process" : type == __priority_which::PRIO_PGRP ? "process group" : \
            type == __priority_which::PRIO_USER ? "user" : "unknown", getErrorInfo(err).c_str());
        return false;
    }

    return true;
}

bool getProcPriority(int *receiver)
{
    assert( nullptr != receiver);

    errno = 0;
    auto prio = getpriority(PRIO_PROCESS,0);
    auto err = errno;
    
    // not only return value, it is unbelievable.
    if(-1 == prio && err)
    {
        // failed.
        qlog_e("get priority(process) failed, %s\n", getErrorInfo(err).c_str());
        return false;
    }
    
    *receiver = prio;
    return true;
}

bool setCpuAffinity(const pid_t pid,const uint32_t cpuNo)
{
    if(pid < 0)
    {
        return false;
    }

    cpu_set_t cpuSet;

    // clear cpu set.
    CPU_ZERO(&cpuSet);

    // add set.
    CPU_SET(cpuNo,&cpuSet);
    
    // bind CPU core and process.
    auto ret = sched_setaffinity(pid,sizeof(cpu_set_t),&cpuSet);
    if(ret < 0)
    {
        int err = errno;
        qlog_e("set cpu affinity(process) failed, %s\n", getErrorInfo(err).c_str()); 
        return false;
    }

    // give a short delay, let os validate it.
    usleep(2000);

    return true;
}

bool setCpuAffinity(const pthread_t tid,const uint32_t cpuNo)
{
    if(tid <= 0)
    {
        return false;
    }

    cpu_set_t cpuSet;

    // clear cpu set.
    CPU_ZERO(&cpuSet);

    // add cpu set.
    CPU_SET(cpuNo,&cpuSet);
    
    // bind CPU core and thread.
    auto err = pthread_setaffinity_np(tid,sizeof(cpu_set_t),&cpuSet);
    if(err != 0)
    {
        qlog_e("set cpu affinity(thread) failed, %s\n", getErrorInfo(err).c_str()); 
        return false;
    }
    
    return true;
}

std::vector<unsigned int> getCpuAffinity(const bool isProcess)
{
    // get CPU core number, std::thread::hardware_concurrency alseo can use.
    uint32_t cpuNum = (uint32_t)sysconf(_SC_NPROCESSORS_CONF);
    if(!cpuNum)
    {
        qlog_e("get cpu number failed, %s,%d\n",__FUNCTION__,__LINE__);
        assert( cpuNum );
    }

    std::vector<unsigned int> cpus;
    cpu_set_t cpuSet = { 0 };
    int ret = -1;

    if(isProcess)
    {
        // query current process's CPU affinity.
        ret = sched_getaffinity(0,sizeof(cpu_set_t),&cpuSet);
        if(ret < 0)
        {
            int err = errno;
            qlog_e("get cpu affinity(process) failed, %s\n", getErrorInfo(err).c_str()); 
            return cpus;
        }

    }
    else
    {
        // query current thread's CPU affinity.
        ret = pthread_getaffinity_np(pthread_self(),sizeof(cpu_set_t),&cpuSet);
        if(ret != 0)
        {
            int err = ret;
            qlog_e("get cpu affinity(thread) failed, %s\n", getErrorInfo(err).c_str()); 
            return cpus;
        }
    }
    
    // query process or thread is runnable on these CPU cores.
    for(uint32_t i = 0; i < cpuNum; ++i)
    {
        if(CPU_ISSET(i,&cpuSet))
        {
            cpus.push_back(i);
        }
    }

    return cpus;
}
